package com.hyh.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hyh.user.dao.UserMapper;
import com.hyh.user.domain.LoginForm;
import com.hyh.user.domain.LoginUser;
import com.hyh.user.domain.User;
import com.hyh.user.result.PageResult;
import com.hyh.user.service.UserService;
import com.hyh.user.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private CacheClient cacheClient;

    @Autowired
    private UserMapper userMapper;

    @Value("${file.upload-dir}")
    private String uploadDir;


    PasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();


    @Override
    public PageResult login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken
                = new UsernamePasswordAuthenticationToken(user.getNumber(),
                user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate.getPrincipal()))
            throw new RuntimeException("用户名或密码错误");
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String number = loginUser.getUser().getNumber();
        User user1 = loginUser.getUser();
        String token = JwtUtil.getToken(loginUser.getUser());
        extracted(user1, token);
        insertImg(user1);
        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        map.put("id", String.valueOf(user1.getId()));
        return new PageResult(HttpStatus.HTTP_OK, map, "登录成功");
    }

    // 将用户信息存入redis当中
    private void extracted(User user1, String token) {
        // 将用户的信息存在redis中
        // 1 将用户user对象转为map stringRedisTemplate保证键值都必须是string
        Map<String, Object> userMap = BeanUtil.beanToMap(user1, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((filedName, filedValue) -> {
                            if (filedValue == null)
                                return null;
                            return filedValue.toString();
                        }));
        //将token number存入redis
        stringRedisTemplate.opsForValue().set(RedisCodeUtil.LOGIN + "token" + user1.getId(), token);
        stringRedisTemplate.expire(RedisCodeUtil.LOGIN + "token" + user1.getId(), RedisCodeUtil.LOGIN_USER_KEY_TTL,
                TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set(RedisCodeUtil.LOGIN + "number" + user1.getId(), user1.getNumber());
        stringRedisTemplate.expire(RedisCodeUtil.LOGIN + "number" + user1.getId(), RedisCodeUtil.LOGIN_USER_KEY_TTL,
                TimeUnit.MINUTES);
        // 2 将map存入redis
        stringRedisTemplate.opsForHash()
                .putAll(RedisCodeUtil.TOKEN_KEY + token, userMap);
        // 3 设置过期时间
        stringRedisTemplate.expire(RedisCodeUtil.TOKEN_KEY + token,
                RedisCodeUtil.LOGIN_USER_KEY_TTL, TimeUnit.MINUTES);
    }

    // 发送手机验证码
    @Override
    public PageResult sendCode(String phone) {
        // 生成验证码
        String code = NumberUtil.RandomCode();
        // 保存到redis当中去 设置过期时间
        stringRedisTemplate.opsForValue().set(RedisCodeUtil.LOGIN_CODE_KEY + phone, code,
                RedisCodeUtil.LOGIN_CODE_KEY_TTL, TimeUnit.MINUTES);
        // 发送验证码
        NumberUtil.send(phone, code);
        return new PageResult(HttpStatus.HTTP_OK, "null", "发送成功");
    }

    // 手机验证码登录
    @Override
    public PageResult loginByCode(LoginForm loginForm) {
        String code = loginForm.getCode();
        String phone = loginForm.getNumber();
        // 从redis中获取code的值
        String cacheCode = stringRedisTemplate.opsForValue().get(RedisCodeUtil.LOGIN_CODE_KEY + phone);
        if (Objects.isNull(cacheCode) || !cacheCode.equals(code))
            return new PageResult(HttpStatus.HTTP_BAD_REQUEST, null, "验证码已错误");
        // 根据手机号查询用户信息
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getNumber, phone);
        User user = userMapper.selectOne(qw);
        if (Objects.isNull(user)) {
            // 创建一个新的用户
            user = createUserWithPhone(phone);
        }
        // 生成token
        String token = JwtUtil.getToken(user);
        extracted(user, token);
        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        map.put("id", String.valueOf(user.getId()));
        return new PageResult(HttpStatus.HTTP_OK, map, "登录成功");
    }

    // 修改用户信息
    @Override
    public PageResult updateUser(User user) {
        if (user.getId() == null)
            return new PageResult(HttpStatus.HTTP_BAD_REQUEST, null, "修改失败");
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        userMapper.updateById(user);
        return new PageResult(HttpStatus.HTTP_OK, null, "修改成功");
    }

    // 根据id查询用户
    @Override
    public PageResult getUserById(Integer id) {
        if (id == null)
            return new PageResult(HttpStatus.HTTP_BAD_REQUEST, null, "查询失败 id不能为空");
        return new PageResult(HttpStatus.HTTP_OK, userMapper.selectById(id), "查询成功");
    }

    // 查询所有用户
    @Override
    public PageResult getAllUser() {
        return new PageResult(HttpStatus.HTTP_OK, userMapper.selectList(null), "查询成功");
    }

    // 重置密码
    @Override
    public PageResult resetPassword(User user) {
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getNumber, user.getNumber());
        User user1 = userMapper.selectOne(qw);
        if (Objects.isNull(user1))
            return new PageResult(HttpStatus.HTTP_BAD_REQUEST, "用户不存在");
        user1.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        userMapper.updateById(user1);
        return new PageResult(HttpStatus.HTTP_OK, null, "重置成功");
    }


    // 上传头像
    @Override
    public PageResult uploadAvatar(MultipartFile file, Long id) {
        // 获取文件名
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        // 拼接文件保存路径
        String filePath = uploadDir + File.separator + fileName;
        // 保存文件
        try {
            file.transferTo(new File(filePath));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        User user = userMapper.selectById(id);
        user.setAvatarImg(fileName);
        userMapper.updateById(user);

        return new PageResult(HttpStatus.HTTP_OK, user, "上传成功");
    }

    // 测试redis工具类 不做功能性检验
    @Override
    public PageResult testRedisUtil(Long id) {
        User user = cacheClient.queryWithPassThrough(RedisCodeUtil.LOGIN_CODE_KEY, id,
                User.class, id2 -> userMapper.selectById(id2),
                5L, TimeUnit.MINUTES);
        return new PageResult(HttpStatus.HTTP_OK, user, "查询成功");
    }

    // 创建一个新的用户
    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setNumber(phone);
        user.setUsername(SystemConstants.NEW_USER + RandomUtil.randomString(10));
        // 保存用户
        userMapper.insert(user);
        return userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getNumber, phone));
    }

    // 生成随机的头像
    public void insertImg(User user) {
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getNumber, user.getNumber());
        User user1 = userMapper.selectOne(qw);
        if (user1.getAvatarImg() == null || user1.getAvatarImg().isEmpty())
            user1.setAvatarImg(ImgUtil.img[RandomUtil.randomInt(0, ImgUtil.img.length)]);
        userMapper.updateById(user1);
    }
}
